﻿using System;
using System.Collections.Generic;
using MagistrAPI.Enums;

namespace MagistrAPI.MathModel
{
    public static class SystemOfEquations
    {
        private static Dictionary<Equations, Delegate> _equations;

        public static Dictionary<Equations, Delegate> Equations
        {
            get
            {
                return _equations ?? (_equations = new Dictionary<Equations, Delegate>(18)
                                                       {
                                                           {Enums.Equations.X, new Func<double[], double>(X)},
                                                           {Enums.Equations.Y, new Func<double[], double>(Y)},
                                                           {Enums.Equations.Z, new Func<double[], double>(Z)},
                                                           {Enums.Equations.Vx, new Func<double[], double>(Vx)},
                                                           {Enums.Equations.Vy, new Func<double[], double>(Vy)},
                                                           {Enums.Equations.Vz, new Func<double[], double>(Vz)},
                                                           {Enums.Equations.Wx, new Func<double[], double>(Wx)},
                                                           {Enums.Equations.Wy, new Func<double[], double>(Wy)},
                                                           {Enums.Equations.Wz, new Func<double[], double>(Wz)},
                                                           {Enums.Equations.Lx, new Func<double[], double>(Lx)},
                                                           {Enums.Equations.Ly, new Func<double[], double>(Ly)},
                                                           {Enums.Equations.Lz, new Func<double[], double>(Lz)},
                                                           {Enums.Equations.Mx, new Func<double[], double>(Mx)},
                                                           {Enums.Equations.My, new Func<double[], double>(My)},
                                                           {Enums.Equations.Mz, new Func<double[], double>(Mz)},
                                                           {Enums.Equations.Nx, new Func<double[], double>(Nx)},
                                                           {Enums.Equations.Ny, new Func<double[], double>(Ny)},
                                                           {Enums.Equations.Nz, new Func<double[], double>(Nz)}
                                                       });
            }
        }

        private static double Nz(double[] arg)
        {
            double nX = arg[15];
            double nY = arg[16];
            double wX = arg[6];
            double wY = arg[7];
            return nX*wY - nY*wX;
        }

        private static double Ny(double[] arg)
        {
            double nX = arg[15];
            double nZ = arg[17];
            double wX = arg[6];
            double wZ = arg[8];
            return nZ*wX - nX*wZ;
        }

        private static double Nx(double[] arg)
        {
            double nY = arg[16];
            double nZ = arg[17];
            double wY = arg[7];
            double wZ = arg[8];
            return nY*wZ - nZ*wY;
        }

        private static double Mz(double[] arg)
        {
            double mX = arg[12];
            double mY = arg[13];
            double wX = arg[6];
            double wY = arg[7];
            return mX*wY - mY*wX;
        }

        private static double My(double[] arg)
        {
            double mX = arg[12];
            double mZ = arg[14];
            double wX = arg[6];
            double wZ = arg[8];
            return mZ*wX - mX*wZ;
        }

        private static double Mx(double[] arg)
        {
            double mY = arg[13];
            double mZ = arg[14];
            double wY = arg[7];
            double wZ = arg[8];
            return mY*wZ - mZ*wY;
        }

        private static double Lz(double[] arg)
        {
            double lX = arg[9];
            double lY = arg[10];
            double wX = arg[6];
            double wY = arg[7];
            return lX*wY - lY*wX;
        }

        private static double Ly(double[] arg)
        {
            double lX = arg[9];
            double lZ = arg[11];
            double wX = arg[6];
            double wZ = arg[8];
            return lZ*wX - lX*wZ;
        }

        private static double Lx(double[] arg)
        {
            double lY = arg[10];
            double lZ = arg[11];
            double wY = arg[7];
            double wZ = arg[8];
            return lY*wZ - lZ*wY;
        }

        private static double Wz(double[] arg)
        {
            double x = arg[0];
            double y = arg[1];
            double z = arg[2];
            double vX = arg[3];
            double vY = arg[4];
            double vZ = arg[5];
            double wX = arg[6];
            double wY = arg[7];
            double lX = arg[9];
            double lY = arg[10];
            double lZ = arg[11];
            double mX = arg[12];
            double mY = arg[13];
            double mZ = arg[14];
            double nX = arg[15];
            double nY = arg[16];
            double nZ = arg[17];
            double mZ1 = MathModelFunc.Mx(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ) *
                         MathModelMatrices.Jv[0, 2] +
                         MathModelFunc.My(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ) *
                         MathModelMatrices.Jv[1, 2] +
                         MathModelFunc.Mz(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ) *
                         MathModelMatrices.Jv[2, 2];
            return mZ1/MathModelConst.Jz - wY*wX*((MathModelConst.Jy - MathModelConst.Jx)/MathModelConst.Jz);

        }

        private static double Wy(double[] arg)
        {
            double x = arg[0];
            double y = arg[1];
            double z = arg[2];
            double vX = arg[3];
            double vY = arg[4];
            double vZ = arg[5];
            double wX = arg[6];
            double wZ = arg[8];
            double lX = arg[9];
            double lY = arg[10];
            double lZ = arg[11];
            double mX = arg[12];
            double mY = arg[13];
            double mZ = arg[14];
            double nX = arg[15];
            double nY = arg[16];
            double nZ = arg[17];
            double mY1 = MathModelFunc.Mx(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ)*
                         MathModelMatrices.Jv[0, 1] +
                         MathModelFunc.My(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ)*
                         MathModelMatrices.Jv[1, 1] +
                         MathModelFunc.Mz(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ)*
                         MathModelMatrices.Jv[2, 1];
            return mY1/MathModelConst.Jy - wX*wZ*((MathModelConst.Jx - MathModelConst.Jz)/MathModelConst.Jy);
        }

        private static double Wx(double[] arg)
        {
            double x = arg[0];
            double y = arg[1];
            double z = arg[2];
            double vX = arg[3];
            double vY = arg[4];
            double vZ = arg[5];
            double wY = arg[7];
            double wZ = arg[8];
            double lX = arg[9];
            double lY = arg[10];
            double lZ = arg[11];
            double mX = arg[12];
            double mY = arg[13];
            double mZ = arg[14];
            double nX = arg[15];
            double nY = arg[16];
            double nZ = arg[17];
            double mX1 = MathModelFunc.Mx(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ) *
                         MathModelMatrices.Jv[0, 0] +
                         MathModelFunc.My(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ) *
                         MathModelMatrices.Jv[1, 0] +
                         MathModelFunc.Mz(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ) *
                         MathModelMatrices.Jv[2, 0];
            return mX1 / MathModelConst.Jx - wY * wZ * ((MathModelConst.Jz - MathModelConst.Jy) / MathModelConst.Jx);
        }

        private static double Vz(double[] arg)
        {
            double[,] dV;
            double[] guu;
            Vxyz(arg, out dV, out guu);
            return dV[2, 0] / MathModelConst.Ma + guu[2];
        }

        private static double Vy(double[] arg)
        {
            double[,] dV;
            double[] guu;
            Vxyz(arg, out dV, out guu);
            return dV[1, 0] / MathModelConst.Ma + guu[1];
        }

        private static double Z(double[] arg)
        {
            double vZ = arg[5];
            return vZ;
        }

        private static double Y(double[] arg)
        {
            double vY = arg[4];
            return vY;
        }

        private static double Vx(double[] arg)
        {
            double[,] dV;
            double[] guu;
            Vxyz(arg, out dV, out guu);
            return dV[0, 0]/MathModelConst.Ma + guu[0];
        }

        private static double X(double[] arg)
        {
            double vX = arg[3];
            return vX;
        }

        private static void Vxyz(double[] arg,out double [,] dV,out double [] guu)
        {
            double x = arg[0];
            double y = arg[1];
            double z = arg[2];
            double vX = arg[3];
            double vY = arg[4];
            double vZ = arg[5];
            double lX = arg[9];
            double lY = arg[10];
            double lZ = arg[11];
            double mX = arg[12];
            double mY = arg[13];
            double mZ = arg[14];
            double nX = arg[15];
            double nY = arg[16];
            double nZ = arg[17];
            dV = MathModelFunc.LsbUy(lX, lY, lZ, mX, mY, mZ, nX, nY, nZ);
            double[] rxyz = MathModelFunc.Rxyz(x, y, z, vX, vY, vZ, lX, lY, lZ, mX, mY, mZ, nX, nY, nZ);
            double[,] r = new[,] { { rxyz[0] }, { rxyz[1] }, { rxyz[2] } };
            dV = MathModelFunc.MatrToMatr(dV, r, 3, 3, 1);
            guu = MathModelFunc.Guu(x, y, z); 
            if (guu == null || dV == null)
            {
                throw new NullReferenceException();
            }
        }
    }
}
